﻿using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;


namespace TCPClient
{
    class HookManager
    {

        #region WIN32 API Functions and Constants

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
    
        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll")]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        private const int WH_KEYBOARD_LL = 13;
        private const int WH_MOUSE_LL = 14;

        private const int WM_KEYDOWN = 0x0100;
        private const int WM_KEYUP = 0x101;

        private const int WM_LBUTTONDOWN = 0x201;
        private const int WM_RBUTTONDOWN = 0x204;
        private const int WM_MBUTTONDOWN = 0x207;
        private const int WM_LBUTTONDBLCLK = 0x203;
        private const int WM_RBUTTONDBLCLK = 0x206;
        private const int WM_MBUTTONDBLCLK = 0x0209;

        private DateTime clickedTime = DateTime.Now;
        private bool IsSecondClick = false;
        private double mouseXold;
        private double mouseYold;
        private MouseButtons mouseButtonold;

        #endregion

        private HookProc _KeyBoardDelegate;
        private HookProc _MouseDelegate;
        private IntPtr _KeyBoardHookHandle = IntPtr.Zero;
        private IntPtr _MouseHookHandle = IntPtr.Zero;

        public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        [StructLayout(LayoutKind.Sequential)]
        public struct KeyboardHookStruct
        {
            public int VirtualKeyCode;
            public int ScanCode;
            public int Flags;
            public int Time;
            public int ExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class MouseHookStruct
        {
            public POINT pt;
            public int hwnd;
            public int wHitTestCode;
            public int dwExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class MouseLLHookStruct
        {
            public POINT pt;
            public int mouseData;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }

        #region KeyBoard Events

        public event KeyEventHandler KeyDown;
        public event KeyEventHandler KeyUp;

        #endregion

        #region MouseClick Events

        public event MouseEventHandler MouseClick;

        #endregion

        ~HookManager()
        {
           Uninstall();
        }

        public void Install()
        {
            _KeyBoardDelegate = KeyboardHookProc;
            _MouseDelegate = MouseHookProc;
            _KeyBoardHookHandle = SetupHook(WH_KEYBOARD_LL,_KeyBoardDelegate);
            _MouseHookHandle = SetupHook(WH_MOUSE_LL, _MouseDelegate);
            
            if (_KeyBoardHookHandle == IntPtr.Zero)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            if (_MouseHookHandle == IntPtr.Zero)
                throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        private IntPtr SetupHook(int X, HookProc hookProc)
        {
            IntPtr hInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);

            return SetWindowsHookEx(X, hookProc, hInstance, 0);
        }

        private IntPtr KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if(nCode >= 0)
            {
                KeyboardHookStruct kbStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));

                if(wParam == (IntPtr)WM_KEYDOWN)
                {
                    if(KeyDown != null)
                        KeyDown(null, new KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
                }
                else if(wParam == (IntPtr)WM_KEYUP)
                {
                    if(KeyUp != null)
                        KeyUp(null,new KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
                }
            }

            return CallNextHookEx(_KeyBoardHookHandle, nCode, wParam, lParam);       
        }

        private IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));

                //if (wParam == (IntPtr)WM_LBUTTONDOWN)
                //{
                //    MouseClick(null, new MouseEventArgs(MouseButtons.Left, 2, 0, 0, 0));
                //}
                //else if (wParam == (IntPtr)WM_RBUTTONDOWN)
                //{
                //    MouseClick(null, new MouseEventArgs(MouseButtons.Right, 2, 0, 0, 0));
                //}
                //else if (wParam == (IntPtr)WM_MBUTTONDOWN)
                //{
                //    MouseClick(null, new MouseEventArgs(MouseButtons.Middle, 2, 0, 0, 0));
                //}
                MouseButtons button = MouseButtons.None;
                short mouseDelta = 0;
                switch ((int)wParam)
                {
                    case WM_LBUTTONDOWN:
                        button = MouseButtons.Left;
                        break;
                    case WM_RBUTTONDOWN:
                        button = MouseButtons.Right;
                        break;
                    case WM_MBUTTONDOWN:
                        button = MouseButtons.Middle;
                        break;
                }

                //double clicks
                int clickCount = 0;
                //if (button != MouseButtons.None)
                //    if ((int)wParam == WM_LBUTTONDBLCLK || (int)wParam == WM_RBUTTONDBLCLK) clickCount = 2;
                //    else clickCount = 1;
                if (button != MouseButtons.None)
            //    if(IsSecondClick)
            //    {
               //  IsSecondClick = false;
                 if ((mouseHookStruct.pt.x == mouseXold)
                     && mouseHookStruct.pt.y == mouseYold
                     && (DateTime.Now - clickedTime).TotalMilliseconds < 500
                     && mouseButtonold == button
                     && IsSecondClick == true)
                     {
                         clickCount = 2;
                         IsSecondClick = false;
                     }
              //  }
                   else
                   {
                       clickCount = 1;
                       IsSecondClick = true;
                       clickedTime = DateTime.Now;
                       mouseXold = mouseHookStruct.pt.x;
                       mouseYold = mouseHookStruct.pt.y;
                       mouseButtonold = button;
                   }

                //generate event 
                MouseEventArgs e = new MouseEventArgs(
                                                   button,
                                                   clickCount,
                                                   mouseHookStruct.pt.x,
                                                   mouseHookStruct.pt.y,
                                                   mouseDelta);
                //raise it
                MouseClick(this, e);
            }

            return CallNextHookEx(_MouseHookHandle, nCode, wParam, lParam);
        }

        public void Uninstall()
        {
            UnhookWindowsHookEx(_KeyBoardHookHandle);
        }
    }
}
